package jp.reflexworks.gae.util;

import com.google.appengine.api.memcache.MemcacheServiceFactory;
import com.google.appengine.api.memcache.MemcacheService;

public class MemcacheUtils {
	
	private MemcacheService cache;
	private long SLEEP_MILLISEC = 200;
	private int NUM_RETRIES = 3;
	
	public MemcacheUtils() {
		cache = MemcacheServiceFactory.getMemcacheService();
	}
	
	/**
	 * Memcacheへ値を登録します
	 * @param key
	 * @param value
	 */
	public void put(Object key, Object value) {
		cache.put(key, value);
	}

	/**
	 * Memcacheから値を取得します
	 * @param key
	 * @return
	 */
	public Object get(Object key) {
		return cache.get(key);
	}

	/**
	 * Memcacheの値を削除します
	 * @param key
	 */
	public void delete(Object key) {
		cache.delete(key);
	}

	/**
	 * Memcacheに指定されたキーが登録されているかチェックします
	 * @param key
	 * @return
	 */
	public boolean contains(Object key) {
		return cache.contains(key);
	}
	
	/**
	 * Memcacheに指定された数値を加えます
	 * @param key
	 * @param value
	 * @return
	 */
	public long increment(Object key, long value) {
		return cache.increment(key, value);
	}

	/**
	 * ロック取得
	 * ロックを解除したい場合、releaseLockを実行してください。
	 * @param key
	 * @return
	 */
	public boolean acquireLock(Object key) {
		if (!contains(key)) {
			put(key, 1l);	// 初期処理
			return true;
		}
		
		for (int r = 0; r <= NUM_RETRIES; r++) {
			long ret = increment(key, 1l);
			if (ret == 1) {
				return true;	// ロック成功
			}
			
			increment(key, -1l);	// ロック失敗、元に戻す。
			if (SLEEP_MILLISEC > 0) {
				try {
					Thread.sleep(SLEEP_MILLISEC);
				} catch (InterruptedException e2) {}
			}
		}
		
		return false;
	}
	
	/**
	 * ロックされているかどうかの判定
	 * acquireLock処理でロックされている場合true、ロックされていない場合falseを返します。
	 * @param key
	 * @return
	 */
	public boolean isLock(Object key) {
		if (!contains(key)) {
			return false;
		}
		long ret = (Long)get(key);
		if (ret == 0) {
			return false;
		}
		return true;
	}

	/**
	 * ロック解除
	 * @param key
	 */
	public void releaseLock(Object key) {
		increment(key, -1l);
	}
	
	/**
	 * ロック処理を行う場合の、リトライ時のスリープ時間を設定します。
	 * @param millisec
	 */
	public void setSleepMillsec(long millisec) {
		SLEEP_MILLISEC = millisec;
	}
	
	/**
	 * ロック処理を行う場合の、リトライ回数を設定します。
	 * @param count
	 */
	public void setRetryCount(int count) {
		if (count < 0) {
			NUM_RETRIES = 0;
		} else {
			NUM_RETRIES = count;
		}
	}

}
